﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using TMCM;

namespace Plotter
{
    class Plotter
    {
        public TMCM1110 XAxis { get; set; }
        public TMCM1110 YAxis { get; set; }
        public int MaxSpeed { get; set; }
        public int StepWidth { get; set; }
        public int StepHeight { get; set; }
        public TMCM1110 PrinterAxis { get; set; }
        
        public event Action Initialized;
        public event Action Drawn;

        private DrawableObject _drawable;

        /// <summary>
        /// Die Plotter Klasse ermöglicht das Zeichnen von IDrawableObject mit Hilfe zweier TMCM1110-Einheiten
        /// </summary>
        /// <param name="x_axis">Die für die X-Achse verantwortliche TMCM1110-Instanz (muss geöffnet sein!)</param>
        /// <param name="y_axis">Die für die Y-Achse verantwortliche TMCM1110-Instanz (muss geöffnet sein!)</param>
        /// <param name="max_speed">Die Geschwindigkeit mit welcher die Motoren drehen</param>
        public Plotter(TMCM1110 x_axis, TMCM1110 y_axis, int max_speed)
        {
            this.XAxis = x_axis;
            this.YAxis = y_axis;
            this.MaxSpeed = max_speed;
            
            this.PrinterAxis = x_axis;

            this.XAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, max_speed);
            this.YAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, max_speed);
        }
        /// <summary>
        /// Die Plotter Klasse ermöglicht das Zeichnen von IDrawableObject mit Hilfe zweier TMCM1110-Einheiten
        /// </summary>
        /// <param name="x_axis">Die für die X-Achse verantwortliche TMCM1110-Instanz (muss geöffnet sein!)</param>
        /// <param name="y_axis">Die für die Y-Achse verantwortliche TMCM1110-Instanz (muss geöffnet sein!)</param>
        public Plotter(TMCM1110 x_axis, TMCM1110 y_axis) : this(x_axis, y_axis, 100) { }

        /// <summary>
        /// Fährt in die Endschalter und kennt somit die Breite/Länge
        /// </summary>
        /// <returns>Erfolgreich?</returns>
        private bool InitializeDimensions()
        {
            this.XAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, 800);
            this.YAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, 800);

            //Vom Schalter weg bewegen
            this.XAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, 50000, TMCM.TMCM.WAIT_TYPE.POS, 0);
            if (this.XAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, 50000000, TMCM.TMCM.WAIT_TYPE.LIMSW, 0))
            {
                this.XAxis.MotorStop();
                this.StepWidth = this.XAxis.GetAxisParameter(TMCM1110.GAP_TYPE.ActualPosition);
                //Andere Achse auch wegbewegen
                this.YAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, 50000, TMCM.TMCM.WAIT_TYPE.POS, 0);
                if (this.YAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, 50000000, TMCM.TMCM.WAIT_TYPE.LIMSW, 0))
                {
                    this.YAxis.MotorStop();
                    this.StepHeight = this.YAxis.GetAxisParameter(TMCM1110.GAP_TYPE.ActualPosition);
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Richtet die Achsen mit Hilfe der ReferenceSearch aus
        /// </summary>
        public void Initialize()
        {
            ThreadPool.QueueUserWorkItem((o) =>
            {
                if (this.XAxis.ReferencSearch() && this.YAxis.ReferencSearch())
                {
                    if (this.InitializeDimensions())
                    {
                        if (this.Initialized != null)
                        {
                            this.Initialized();
                        }
                    }
                    else
                    {
                        throw new Exception("Es ist ein Fehler bei der Dimensionsberechnung aufgetreten");
                    }
                }
                else
                {
                    throw new Exception("Bei der Referenzsuche ist ein Fehler aufgetreten");
                }
            });
        }

        /// <summary>
        /// Zeichnet ein IDrawableObject und löst nach Ende das Drawn-Event aus
        /// </summary>
        /// <param name="drawable_object">Das zu zeichnende Objekt</param>
        public void Draw(DrawableObject drawable_object)
        {
            drawable_object.MapPositions(this);
            this._drawable = drawable_object;

            //Beide zum Anfangspunkt fahren
            System.Drawing.Point start_point = this._drawable.MappedPoints[0];
            if (this.XAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.ABS, start_point.X, TMCM.TMCM.WAIT_TYPE.POS, 0) && this.YAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.ABS, start_point.Y, TMCM.TMCM.WAIT_TYPE.POS, 0))
            {
                this.Print();
                //Werte vom alten Durchlauf zurücksetzen
                counter = 1;
                index = -1;
                this.Continue();
            }
            else
            {
                throw new Exception("Drawing error!");
            }
            
        }

        int counter = 1;
        int index = -1;
        /// <summary>
        /// Rekursive Funktion, welche nach jedem Schritt die beiden Achsen-Threads synchronsisiert
        /// </summary>
        public void Continue()
        {
            counter++;
            if (counter == 2)
            {
                counter = 0;
                index++;
                if (index < this._drawable.XPlotterLines.Count)
                {
                    ThreadPool.QueueUserWorkItem((o) =>
                    {
                        PlotterLine line = this._drawable.XPlotterLines[index];
                        this.XAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, (int)((double)this.MaxSpeed * line.MaxSpeedFactor));
                        this.XAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, line.Distance, TMCM.TMCM.WAIT_TYPE.POS, 0);
                        this.Continue();
                    });
                    ThreadPool.QueueUserWorkItem((o) =>
                    {
                        PlotterLine line = this._drawable.YPlotterLines[index];
                        this.YAxis.SetAxisParameter(TMCM1110.SAP_TYPE.MaxPositioningSpeed, (int)((double)this.MaxSpeed * line.MaxSpeedFactor));
                        this.YAxis.MoveToPosition(TMCM.TMCM.MVP_TYPE.REL, line.Distance, TMCM.TMCM.WAIT_TYPE.POS, 0);
                        this.Continue();
                    });
                }
                else
                {
                    this.ReleasePrint();
                    if (this.Drawn != null)
                    {
                        this.Drawn();
                    }
                }
            }
        }

        /// <summary>
        /// Stift runter drücken
        /// </summary>
        private void Print()
        {
            this.PrinterAxis.SetOutput(TMCM1110.SIO_TYPE.DOUT2, 2, 1);
        }
        /// <summary>
        /// Stift wieder lösen
        /// </summary>
        private void ReleasePrint()
        {
            this.PrinterAxis.SetOutput(TMCM1110.SIO_TYPE.DOUT2, 2, 0);
        }
    }
}
